class DepthManager(object):
    """
    Manage the depth of the alphabeta algorithm.
    """

    DEFAULT_SIZE = 5
    DEFAULT_DEPTH = 6
    MAX_MULTIPLIER = 1.1

    def __init__(self):
        """
        Constructor.
        """
        
        #default values. Updated when the board is received.
        self.board_size = None
        self.possible_moves = None
        self.ply_number = None
        self.max_number_of_moves_to_calculate = self.calculate_max_number_of_moves()
        self.depth = None
    
    def _init(self):
        """ 
        Initialization of the manager. Should be called whenever board_size 
        changes. 
        """
        
        self.possible_moves = self.board_size ** 2
        self.ply_number = 0
        self.depth = self.calculate_max_depth()
    
    def _check_board(self, board):
        """ 
        Checks that the size of the board is not different from the one stored.
        If the value has changed, the initialization is run again.
        """
        
        if board.size != self.board_size:
            self.board_size = board.size
            self._init()

    def calculate_max_number_of_moves(self):
        """
        Calculate the max number of moves.
        """

        possible_moves = self.DEFAULT_SIZE * self.DEFAULT_SIZE
        return int(self.MAX_MULTIPLIER * self.fact(possible_moves, self.DEFAULT_DEPTH))

    def calculate_max_depth(self):
        """
        Calculate the max depth.
        """

        initial_number_of_moves = self.possible_moves - self.ply_number
        max_depth = 1

        while self.max_number_of_moves_to_calculate > self.fact(initial_number_of_moves, max_depth):
            max_depth += 1

            if max_depth == 13:
                break

        max_depth -= 1

        return max_depth

    def fact(self, value, depth):
        """
        Calculates value! / (value - depth)!
        """

        if depth == 0 or value == 1:
            return 1

        return value * self.fact(value - 1, depth - 1)

    def update_depth(self, board):
        """
        Update the depth if possible.
        """
        
        self._check_board(board)

        if self.fact(self.possible_moves - self.ply_number, self.depth + 1) < self.max_number_of_moves_to_calculate:
            self.depth += 1

        self.ply_number += 2